﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
using FriendlyURL.Web.Domain.Abstract;


namespace FriendlyURL.Web.Domain.Impl
{
    public class Cache : ICache
    {
        private System.Web.Caching.Cache cache;
        private TimeSpan timeSpan = new TimeSpan(
            GlobalProperties.DEFAULT_CACHE_DURATION_DAYS,
            GlobalProperties.DEFAULT_CACHE_DURATION_HOURS,
            GlobalProperties.DEFAULT_CACHE_DURATION_MINUTES,
            GlobalProperties.DEFAULT_CACHE_DURATION_SECONDS);

        /// <summary>
        /// Constructor for Cache()
        /// <remarks>
        /// We then define a static constructor for our cache object. This basically means that our cache object is refreshed prior 
        /// to any cache object being created. The exact time can't be determined but can possibly be done at the time that the 
        /// assembly is loaded.
        /// </remarks>
        /// </summary>
        public Cache()
        {
            cache = HttpContext.Current.Cache;
        }

        /// <summary>
        /// Used to retreive something from the Cache
        /// <remarks>
        /// This method simply requires a key value to be passed to it. We then use the cache implementation of cache implementation of 
        /// implementation of Get() and return the value. Keep in mind that this could return the cached item or a NULL value.
        /// </remarks>
        /// </summary>
        /// <param name="cache_key"></param>
        /// <returns></returns>
        public object Get(string cache_key)
        {
            return cache.Get(cache_key);
        }


        /// <summary>
        /// This method returns a generic list of type string.
        /// <remarks>
        /// The way it works is that we first define a keys List that we can add our keys to. We then declare an IDictionaryEnumerator and assign 
        /// the cache.GetEnumerator() values to it. Once we have the Enumeration defined, we iterate through each item in the collection by checking 
        /// the ca.MoveNext() method. With each iteration, we add the key value to our keys collection. We then return the keys collection.
        /// </remarks>
        /// </summary>
        /// <returns></returns>
        public List<string> GetCacheKeys()
        {
            List<string> keys = new List<string>();
            IDictionaryEnumerator ca = cache.GetEnumerator();
            while (ca.MoveNext())
            {
                keys.Add(ca.Key.ToString());
            }
            return keys;
        }

        #region Set Methods
        /// <summary>
        /// Set Methods
        /// <remarks>
        /// This allows us to add items to the cache. Each method is a simple wrapper for all the Set methods that the cache object provides. All these methods 
        /// require that a string key be provided along with the object that is to be cached. Some of them allow for various time-outs to be specified. Others 
        /// allow you to additionally provide a priority for the cached items expiration.
        /// </remarks>
        /// </summary>
        public void Set(string cache_key, object cache_object)
        {
            Set(cache_key, cache_object, timeSpan);
        }

        public void Set(string cache_key, object cache_object, DateTime expiration)
        {
            Set(cache_key, cache_object, expiration, CacheItemPriority.Normal);
        }

        public void Set(string cache_key, object cache_object, TimeSpan expiration)
        {
            Set(cache_key, cache_object, expiration, CacheItemPriority.Normal);
        }

        public void Set(string cache_key, object cache_object, DateTime expiration, CacheItemPriority priority)
        {
            cache.Insert(cache_key, cache_object, null, expiration, System.Web.Caching.Cache.NoSlidingExpiration, priority, null);
        }

        public void Set(string cache_key, object cache_object, TimeSpan expiration, CacheItemPriority priority)
        {
            cache.Insert(cache_key, cache_object, null, System.Web.Caching.Cache.NoAbsoluteExpiration, expiration, priority, null);
        }

        #endregion

        /// <summary>
        /// Delete a cached item.
        /// <remarks>
        /// It accepts the key to be deleted. The method then checks to see if the key exists and removes the key from the cache collection.
        /// </remarks>
        /// </summary>
        /// <param name="cache_key"></param>
        public void Delete(string cache_key)
        {
            if (Exists(cache_key))
            {
                cache.Remove(cache_key);
            }
        }

        /// <summary>
        /// Checks to see if an item is still in the cache collection.
        /// <remarks>
        /// 
        /// The reason for this method is that while items can be freely added to the cache collection, you can never count on them being there when 
        /// you try accessing them the next time. The item could be removed or may not exist for several reasons:    
        ///     -The item may have timed out and been removed
        ///     -It may have been pushed out of the collection due to the presence of many other new items added to the collection
        ///     -The collection may have been re-initialized intentionally, or due to some glitch in the system
        /// This method returns a true or false value based on whether the key that is being checked exists in the collection or not. This only checks 
        /// whether the key exists. It does not pull the item out of the collection and cast it to the appropriate type. The key value may exist while 
        /// the item may not. For this reason, always check that your casted item is not null before using it!
        ///</remarks>
        /// </summary>
        /// <param name="cache_key"></param>
        /// <returns></returns>
        public bool Exists(string cache_key)
        {
            if (cache[cache_key] != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Clear the entire cache collection.
        /// <remarks>
        /// This is easily accomplished by iterating through all the keys in the collection and calling Delete on each of them.
        /// </remarks>
        /// </summary>
        public void Flush()
        {
            foreach (string s in GetCacheKeys())
            {
                Delete(s);
            }
        }
    }
}
